home *** CD-ROM | disk | FTP | other *** search
/ Java Developer's Companion / Java Developer's Companion.iso / Javacup / IN231VFD.TAR / internet / IN231VFD / PackerLayout.java < prev    next >
Text File  |  1996-05-21  |  18KB  |  669 lines

  1. /*
  2.  * @(#)PackerLayout.java    1.0 95/11/10 Daeron Meyer
  3.  *
  4.  * Author: Daeron Meyer
  5.  * Copyright (c) 1995 by The Geometry Center.
  6.  * Distributed under the terms of the GNU Library General Public License.
  7.  *
  8.  */
  9.  
  10. import java.lang.*;
  11. import java.awt.*;
  12. import java.util.*;
  13.  
  14. /**
  15.  * PackerLayout is used to lay out widget components.
  16.  *
  17.  * @version     1.0, 95/11/10
  18.  * @author     Daeron Meyer
  19.  *
  20.  * Based heavily on the work of John Ousterhout, creator of the Tk Toolkit.
  21.  *
  22.  */
  23. public class PackerLayout extends Object implements LayoutManager {
  24.  
  25.     Hashtable compinfo;
  26.     Hashtable nameinfo;
  27.     Component firstcomp, lastcomp;
  28.  
  29.     public static final String initText =
  30.                 "PackerLayout (c) 1995 by Daeron Meyer\n";
  31.  
  32.     static final String F_ANCHOR    = "anchor";
  33.     static final String F_EXPAND    = "expand";
  34.     static final String F_FILL        = "fill";
  35.     static final String F_FILLX        = "fillx";
  36.     static final String F_FILLY        = "filly";
  37.     static final String F_IPADX        = "ipadx";
  38.     static final String F_IPADY        = "ipady";
  39.     static final String F_PADX        = "padx";
  40.     static final String F_PADY        = "pady";
  41.     static final String F_SIDE        = "side";
  42.     static final String F_NAME        = "name";
  43.  
  44.     static final String ANCH_TOK_N    = "n";
  45.     static final String ANCH_TOK_NE    = "ne";
  46.     static final String ANCH_TOK_E    = "e";
  47.     static final String ANCH_TOK_SE    = "se";
  48.     static final String ANCH_TOK_S    = "s";
  49.     static final String ANCH_TOK_SW    = "sw";
  50.     static final String ANCH_TOK_W    = "w";
  51.     static final String ANCH_TOK_NW    = "nw";
  52.     static final String ANCH_TOK_CENTER = "center";    // default value
  53.  
  54.     static final int ANCHOR_N        = 0;
  55.     static final int ANCHOR_NE        = 1;
  56.     static final int ANCHOR_E        = 2;
  57.     static final int ANCHOR_SE        = 3;
  58.     static final int ANCHOR_S        = 4;
  59.     static final int ANCHOR_SW        = 5;
  60.     static final int ANCHOR_W        = 6;
  61.     static final int ANCHOR_NW        = 7;
  62.     static final int ANCHOR_CENTER    = 8;        // default value
  63.  
  64.     static final String EXPAND_TRUE    = "true";
  65.     static final String EXPAND_FALSE    = "false";    // default value
  66.     static final String EXPAND_YES    = "1";
  67.     static final String EXPAND_NO    = "0";
  68.  
  69.     static final String FILL_NONE    = "none";    // default value
  70.     static final String FILL_X        = "x";
  71.     static final String FILL_Y        = "y";
  72.     static final String FILL_BOTH    = "both";
  73.  
  74.     static final String SIDE_TOP    = "top";    // default value
  75.     static final String SIDE_BOTTOM    = "bottom";
  76.     static final String SIDE_LEFT    = "left";
  77.     static final String SIDE_RIGHT    = "right";
  78.  
  79.     static final String CHAR_SEMI    = ";";
  80.     static final String CHAR_EQUAL    = "=";
  81.     static final String CHAR_ALL    = CHAR_SEMI + CHAR_EQUAL;
  82.  
  83.     static final String COMPONENT_NEXT    = "next";
  84.     static final String COMPONENT_PREV    = "prev";
  85.  
  86.  
  87.     static final int debug        = 0;    // set the debug level
  88.  
  89.     /**
  90.      * Constructs a new Packer Layout.
  91.      */
  92.     public PackerLayout() {
  93.  
  94.       compinfo = new Hashtable();
  95.       nameinfo = new Hashtable();
  96.       firstcomp = null;
  97.       lastcomp = null;
  98.  
  99.     }
  100.  
  101.     /**
  102.      * Adds the specified component to the layout.
  103.      * @param name information about attachments
  104.      * @param comp the the component to be added
  105.      */
  106.     public void addLayoutComponent(String name, Component comp) {
  107.  
  108.       String realname = null;
  109.       String tok, val;
  110.       Hashtable packtable = new Hashtable();
  111.  
  112.       try {
  113.     if (comp == null) return;
  114.     StringTokenizer st = new StringTokenizer(name, CHAR_ALL, true);
  115.  
  116.     realname = new String(st.nextToken());
  117.  
  118.     // Set default values for each component:
  119.  
  120.     packtable.put(F_ANCHOR,    new Integer(ANCHOR_CENTER));
  121.     packtable.put(F_EXPAND,    new Boolean(false));
  122.     packtable.put(F_FILLX,    new Boolean(false));
  123.     packtable.put(F_FILLY,    new Boolean(false));
  124.     packtable.put(F_IPADX,    new Integer(0));
  125.     packtable.put(F_IPADY,    new Integer(0));
  126.     packtable.put(F_PADX,    new Integer(0));
  127.     packtable.put(F_PADY,    new Integer(0));
  128.     packtable.put(F_SIDE,    SIDE_TOP);
  129.  
  130.     nameinfo.put(realname, comp);
  131.     compinfo.put(comp, packtable);
  132.     packtable.put(F_NAME, realname);
  133.  
  134.     while(st.hasMoreTokens()) {
  135.  
  136.       if (st.nextToken().equals(CHAR_SEMI)) {
  137.  
  138.         if (st.hasMoreTokens())
  139.           tok = st.nextToken();
  140.         else
  141.           return;
  142.  
  143.         if (!st.nextToken().equals(CHAR_EQUAL))
  144.         throw new NoSuchElementException();
  145.  
  146.         if (tok.equals(F_ANCHOR)) {
  147.  
  148.           val = st.nextToken();
  149.  
  150.           if (val.equals(ANCH_TOK_N))
  151.         packtable.put(tok, new Integer(ANCHOR_N));
  152.           else if (val.equals(ANCH_TOK_NE))
  153.         packtable.put(tok, new Integer(ANCHOR_NE));
  154.           else if (val.equals(ANCH_TOK_E))
  155.         packtable.put(tok, new Integer(ANCHOR_E));
  156.           else if (val.equals(ANCH_TOK_SE))
  157.         packtable.put(tok, new Integer(ANCHOR_SE));
  158.           else if (val.equals(ANCH_TOK_S))
  159.         packtable.put(tok, new Integer(ANCHOR_S));
  160.           else if (val.equals(ANCH_TOK_SW))
  161.         packtable.put(tok, new Integer(ANCHOR_SW));
  162.           else if (val.equals(ANCH_TOK_W))
  163.         packtable.put(tok, new Integer(ANCHOR_W));
  164.           else if (val.equals(ANCH_TOK_NW))
  165.         packtable.put(tok, new Integer(ANCHOR_NW));
  166.           else if (val.equals(ANCH_TOK_CENTER))
  167.         packtable.put(tok, new Integer(ANCHOR_CENTER));
  168.           else throw new NoSuchElementException();
  169.  
  170.         } else if (tok.equals(F_EXPAND)) {
  171.  
  172.           val = st.nextToken();
  173.  
  174.           if (val.equals(EXPAND_TRUE) || val.equals(EXPAND_YES))
  175.         packtable.put(tok, new Boolean(true));
  176.           else
  177.             if (val.equals(EXPAND_FALSE) || val.equals(EXPAND_NO))
  178.           packtable.put(tok, new Boolean(false));
  179.             else throw new NoSuchElementException();
  180.  
  181.         } else if (tok.equals(F_FILL)) {
  182.  
  183.           val = st.nextToken();
  184.  
  185.           if (val.equals(FILL_NONE)) {
  186.  
  187.             packtable.put(new String(F_FILLX), new Boolean(false));
  188.             packtable.put(new String(F_FILLY), new Boolean(false));
  189.  
  190.           } else if (val.equals(FILL_X)) {
  191.  
  192.             packtable.put(new String(F_FILLX), new Boolean(true));
  193.             packtable.put(new String(F_FILLY), new Boolean(false));
  194.  
  195.           } else if (val.equals(FILL_Y)) {
  196.  
  197.             packtable.put(new String(F_FILLX), new Boolean(false));
  198.             packtable.put(new String(F_FILLY), new Boolean(true));
  199.  
  200.           } else if (val.equals(FILL_BOTH)) {
  201.  
  202.             packtable.put(new String(F_FILLX), new Boolean(true));
  203.             packtable.put(new String(F_FILLY), new Boolean(true));
  204.  
  205.           } else throw new NoSuchElementException();
  206.  
  207.         } else if (tok.equals(F_IPADX) || tok.equals(F_IPADY)
  208.           || tok.equals(F_PADX) || tok.equals(F_PADY)) {
  209.  
  210.           val = st.nextToken();
  211.           packtable.put(tok,Integer.valueOf(val));
  212.  
  213.         } else if (tok.equals(F_SIDE)) {
  214.  
  215.           val = st.nextToken();
  216.  
  217.           if (val.equals(SIDE_TOP) || val.equals(SIDE_LEFT)
  218.         || val.equals(SIDE_RIGHT) || val.equals(SIDE_BOTTOM)) {
  219.  
  220.             packtable.put(tok, val);
  221.  
  222.           } else throw new NoSuchElementException();
  223.  
  224.         }
  225.  
  226.           } else throw new NoSuchElementException();
  227.  
  228.     }
  229.  
  230.       } catch (Exception e) {
  231.  
  232.     if (realname != null) {
  233.  
  234.       System.out.println("PackerLayout: Syntax error in component: "
  235.                 + realname);
  236.       nameinfo.remove(realname);
  237.       compinfo.remove(comp);
  238.  
  239.     }
  240.  
  241.         return;
  242.  
  243.       }
  244.  
  245.       if (firstcomp == null) {
  246.  
  247.     firstcomp = comp;
  248.     lastcomp = comp;
  249.  
  250.       } else {
  251.  
  252.     Hashtable opack = (Hashtable) compinfo.get(lastcomp);
  253.     opack.put(COMPONENT_NEXT, comp);
  254.     packtable.put(COMPONENT_PREV, lastcomp);
  255.     lastcomp = comp;
  256.  
  257.       }
  258.  
  259.     }
  260.  
  261.     /**
  262.      * Removes the specified component from the layout.
  263.      * @param comp the component to remove
  264.      */
  265.     public void removeLayoutComponent(Component comp) {
  266.  
  267.       Component prev, next;
  268.       Hashtable opack = (Hashtable) compinfo.get(comp);
  269.       prev = (Component) opack.get(COMPONENT_PREV);
  270.       next = (Component) opack.get(COMPONENT_NEXT);
  271.  
  272.       if (prev == null) { // If we are removing the first component
  273.  
  274.     firstcomp = next;
  275.  
  276.       }
  277.  
  278.       if (next == null) { // If we are removing the last component
  279.  
  280.     lastcomp = prev;
  281.  
  282.       }
  283.  
  284.       if (prev != null) {
  285.  
  286.     Hashtable npack = (Hashtable) compinfo.get(prev);
  287.  
  288.     if (next != null)
  289.       npack.put(COMPONENT_NEXT, next);
  290.     else
  291.       npack.remove(COMPONENT_NEXT);
  292.  
  293.       }
  294.  
  295.       if (next != null) {
  296.  
  297.     Hashtable npack = (Hashtable) compinfo.get(next);
  298.  
  299.     if (prev != null)
  300.       npack.put(COMPONENT_PREV, next);
  301.     else
  302.       npack.remove(COMPONENT_PREV);
  303.  
  304.       }
  305.  
  306.       compinfo.remove(comp);
  307.  
  308.     }
  309.  
  310.  
  311.     /**
  312.      * Returns the preferred dimensions for this layout given the
  313.      * components in the specified target container.
  314.      * @param target the component which needs to be laid out
  315.      * @see Container
  316.      * @see #minimumSize
  317.      */
  318.     public Dimension preferredLayoutSize(Container target) {
  319.  
  320.     Dimension dim = minimumLayoutSize(target);
  321.     Dimension cdim = target.size();
  322.  
  323.     if (cdim.width < dim.width)
  324.       cdim.width = dim.width;
  325.     if (cdim.height < dim.height)
  326.       cdim.height = dim.height;
  327.     return cdim;
  328.  
  329.     }
  330.  
  331.     /**
  332.      * Returns the minimum dimensions needed to layout the
  333.      * components contained in the specified target container.
  334.      * @param target the component which needs to be laid out 
  335.      * @see #preferredSize
  336.      */
  337.     public Dimension minimumLayoutSize(Container target) {
  338.  
  339.     Insets insets = target.insets();
  340.     Dimension dim = new Dimension(0, 0);
  341.     Dimension d, dmax = new Dimension(0, 0);
  342.     int nmembers = target.countComponents();
  343.  
  344.     for (int i = 0; i < nmembers; i++) {
  345.  
  346.       Component m = target.getComponent(i);
  347.       d = m.minimumSize();
  348.  
  349.       Hashtable ptable    = (Hashtable) compinfo.get(m);
  350.  
  351.       if (ptable == null)
  352.         break;
  353.  
  354.       if (debug > 0) {
  355.         String realname = (String) ptable.get(F_NAME);
  356.         System.out.println(realname + " minimum size: "
  357.                     + String.valueOf(d.width) + "x"
  358.                     + String.valueOf(d.height));
  359.       }
  360.  
  361.       int padx        = ((Integer)ptable.get(F_PADX)).intValue()*2;
  362.       int pady        = ((Integer)ptable.get(F_PADY)).intValue()*2;
  363.       int ipadx        = ((Integer)ptable.get(F_IPADX)).intValue();
  364.       int ipady        = ((Integer)ptable.get(F_IPADY)).intValue();
  365.       String side        = (String)ptable.get(F_SIDE);
  366.  
  367.       d.width += padx + ipadx + dim.width;
  368.       d.height += pady + ipady + dim.height;
  369.  
  370.       if (side.equals(SIDE_TOP) || side.equals(SIDE_BOTTOM)) {
  371.  
  372.         if (d.width > dmax.width)
  373.           dmax.width = d.width;
  374.  
  375.         dim.height = d.height;
  376.  
  377.       } else {
  378.  
  379.         if (d.height > dmax.height)
  380.           dmax.height = d.height;
  381.  
  382.         dim.width = d.width;
  383.       }
  384.  
  385.     }
  386.  
  387.     if (dim.width > dmax.width)
  388.       dmax.width = dim.width;
  389.  
  390.     if (dim.height > dmax.height)
  391.       dmax.height = dim.height;
  392.  
  393.     dmax.width += (insets.left + insets.right);
  394.     dmax.height += (insets.top + insets.bottom);
  395.  
  396.     if (debug > 0) {
  397.       System.out.println("Insets: " + String.valueOf(insets.left) + " " +
  398.         String.valueOf(insets.right) + " " +
  399.         String.valueOf(insets.top) + " " +
  400.         String.valueOf(insets.bottom));
  401.         System.out.println("Container minimum size: "
  402.                     + String.valueOf(dmax.width) + "x"
  403.                     + String.valueOf(dmax.height));
  404.     }
  405.  
  406.     return dmax;
  407.     }
  408.  
  409.     /**
  410.      * Lays out the container. This method will actually reshape the
  411.      * components in target in order to satisfy the constraints.
  412.      * @param target the specified component being laid out.
  413.      * @see Container
  414.      */
  415.  
  416.  
  417.     public void layoutContainer(Container target) {
  418.  
  419.     Insets insets = target.insets();
  420.     Dimension dim = target.size();
  421.     int cavityX = 0, cavityY = 0;
  422.     int cavityWidth = dim.width - (insets.left + insets.right);
  423.     int cavityHeight = dim.height - (insets.top + insets.bottom);
  424.     int frameX, frameY, frameWidth, frameHeight;
  425.     int width, height, x, y;
  426.     Component current = firstcomp;
  427.  
  428.  
  429.     if (debug > 0) {
  430.       System.out.println("Laying out container at size: " +
  431.         String.valueOf(cavityWidth) + "x" +
  432.         String.valueOf(cavityHeight));
  433.     }
  434.  
  435.     while (current != null) {
  436.  
  437.       Hashtable ptable    = (Hashtable) compinfo.get(current);
  438.       String side        = (String)ptable.get(F_SIDE);
  439.       int padx        = ((Integer)ptable.get(F_PADX)).intValue()*2;
  440.       int pady        = ((Integer)ptable.get(F_PADY)).intValue()*2;
  441.       int ipadx        = ((Integer)ptable.get(F_IPADX)).intValue();
  442.       int ipady        = ((Integer)ptable.get(F_IPADY)).intValue();
  443.       boolean expand    =((Boolean)ptable.get(F_EXPAND)).booleanValue();
  444.       boolean fillx        =((Boolean)ptable.get(F_FILLX)).booleanValue();
  445.       boolean filly        =((Boolean)ptable.get(F_FILLY)).booleanValue();
  446.       int anchor        = ((Integer)ptable.get(F_ANCHOR)).intValue();
  447.       String name        = (String)ptable.get(F_NAME);
  448.  
  449.       current.layout();
  450.  
  451.       if (side.equals(SIDE_TOP) || side.equals(SIDE_BOTTOM)) {
  452.  
  453.         frameWidth = cavityWidth;
  454.         frameHeight = current.preferredSize().height + pady + ipady;
  455.  
  456.         if (expand)
  457.           frameHeight += YExpansion(current, cavityHeight);
  458.  
  459.         cavityHeight -= frameHeight;
  460.  
  461.         if (cavityHeight < 0) {
  462.           frameHeight += cavityHeight;
  463.           cavityHeight = 0;
  464.         }
  465.  
  466.         frameX = cavityX;
  467.  
  468.         if (side.equals(SIDE_TOP)) {
  469.           frameY = cavityY;
  470.           cavityY += frameHeight;
  471.         } else {
  472.           frameY = cavityY + cavityHeight;
  473.         }
  474.  
  475.       } else {
  476.         frameHeight = cavityHeight;
  477.         frameWidth = current.preferredSize().width + padx + ipadx;
  478.  
  479.         if (expand)
  480.           frameWidth += XExpansion(current, cavityWidth);
  481.  
  482.         cavityWidth -= frameWidth;
  483.         if (cavityWidth < 0) {
  484.           frameWidth += cavityWidth;
  485.           cavityWidth = 0;
  486.         }
  487.         frameY = cavityY;
  488.         if (side.equals(SIDE_LEFT)) {
  489.           frameX = cavityX;
  490.           cavityX += frameWidth;
  491.         } else {
  492.           frameX = cavityX + cavityWidth;
  493.         }
  494.       }
  495.  
  496. // Now that we have the frame size find out the actual component size
  497.  
  498.       width = current.preferredSize().width + ipadx;
  499.  
  500.       if (fillx || (width > (frameWidth - padx)))
  501.           width = frameWidth - padx;
  502.  
  503.       height = current.preferredSize().height + ipady;
  504.  
  505.       if (filly || (height > (frameHeight - pady)))
  506.           height = frameHeight - pady;
  507.  
  508.       padx /= 2; pady /= 2;
  509.  
  510.       switch (anchor) {
  511.         case ANCHOR_N:
  512.           x = frameX + (frameWidth - width)/2;
  513.           y = frameY + pady;
  514.           break;
  515.         case ANCHOR_NE:
  516.           x = frameX + frameWidth - width - padx;
  517.           y = frameY + pady;
  518.           break;
  519.         case ANCHOR_E:
  520.           x = frameX + frameWidth - width - padx;
  521.           y = frameY + (frameHeight - height)/2;
  522.           break;
  523.         case ANCHOR_SE:
  524.           x = frameX + frameWidth - width - padx;
  525.           y = frameY + frameHeight - height - pady;
  526.           break;
  527.         case ANCHOR_S:
  528.           x = frameX + (frameWidth - width)/2;
  529.           y = frameY + frameHeight - height - pady;
  530.           break;
  531.         case ANCHOR_SW:
  532.           x = frameX + padx;
  533.           y = frameY + frameHeight - height - pady;
  534.           break;
  535.         case ANCHOR_W:
  536.           x = frameX + padx;
  537.           y = frameY + (frameHeight - height)/2;
  538.           break;
  539.         case ANCHOR_NW:
  540.           x = frameX + padx;
  541.           y = frameY + pady;
  542.           break;
  543.         case ANCHOR_CENTER:
  544.         default:
  545.           x = frameX + (frameWidth - width)/2;
  546.           y = frameY + (frameHeight - height)/2;
  547.           break;
  548.       }
  549.  
  550.  
  551.     if (debug > 0) {
  552.            System.out.println("Component size: "
  553.                     + String.valueOf(width) + "x"
  554.                     + String.valueOf(height));
  555.     }
  556.       current.reshape(insets.left + x, y + insets.top, width, height);
  557.       current = (Component) ptable.get(COMPONENT_NEXT);
  558.  
  559.     }
  560.     }
  561.  
  562.     int XExpansion(Component current, int cavityWidth) {
  563.  
  564.       Hashtable ptable = (Hashtable) compinfo.get(current);
  565.       int numExpand, minExpand, curExpand;
  566.       int childWidth;
  567.  
  568.       minExpand = cavityWidth;
  569.       numExpand = 0;
  570.  
  571.       for (;current!=null;current=(Component)ptable.get(COMPONENT_NEXT)) {
  572.  
  573.     ptable        = (Hashtable) compinfo.get(current);
  574.         int padx    = ((Integer)ptable.get(F_PADX)).intValue()*2;
  575.         int ipadx    = ((Integer)ptable.get(F_IPADX)).intValue();
  576.     boolean expand    = ((Boolean)ptable.get(F_EXPAND)).booleanValue();
  577.         String side    = (String)ptable.get(F_SIDE);
  578.     childWidth    = current.preferredSize().width + padx + ipadx;
  579.  
  580.     if (side.equals(SIDE_TOP) || side.equals(SIDE_BOTTOM)) {
  581.  
  582.       curExpand = (cavityWidth - childWidth)/numExpand;
  583.  
  584.       if (curExpand < minExpand)
  585.         minExpand = curExpand;
  586.  
  587.     } else {
  588.  
  589.       cavityWidth -= childWidth;
  590.  
  591.       if (expand)
  592.         numExpand++;
  593.  
  594.     }
  595.  
  596.       }
  597.  
  598.       curExpand = cavityWidth/numExpand;
  599.  
  600.       if (curExpand < minExpand)
  601.     minExpand = curExpand;
  602.  
  603.       if (minExpand < 0)
  604.     return 0;
  605.       else
  606.     return minExpand;
  607.  
  608.     }
  609.  
  610.     int YExpansion(Component current, int cavityHeight) {
  611.  
  612.       Hashtable ptable = (Hashtable) compinfo.get(current);
  613.       int numExpand, minExpand, curExpand;
  614.       int childHeight;
  615.  
  616.       minExpand = cavityHeight;
  617.       numExpand = 0;
  618.  
  619.       for (;current!=null;current=(Component)ptable.get(COMPONENT_NEXT)) {
  620.  
  621.     ptable        = (Hashtable) compinfo.get(current);
  622.         int pady    = ((Integer)ptable.get(F_PADY)).intValue()*2;
  623.         int ipady    = ((Integer)ptable.get(F_IPADY)).intValue();
  624.     boolean expand    = ((Boolean)ptable.get(F_EXPAND)).booleanValue();
  625.         String side    = (String)ptable.get(F_SIDE);
  626.     childHeight    = current.preferredSize().height + pady + ipady;
  627.  
  628.     if (side.equals(SIDE_LEFT) || side.equals(SIDE_RIGHT)) {
  629.  
  630.       curExpand = (cavityHeight - childHeight)/numExpand;
  631.  
  632.       if (curExpand < minExpand)
  633.         minExpand = curExpand;
  634.  
  635.     } else {
  636.  
  637.       cavityHeight -= childHeight;
  638.  
  639.       if (expand) {
  640.         numExpand++;
  641.  
  642.       }
  643.  
  644.     }
  645.  
  646.       }
  647.  
  648.       curExpand = cavityHeight/numExpand;
  649.  
  650.       if (curExpand < minExpand)
  651.     minExpand = curExpand;
  652.  
  653.       if (minExpand < 0)
  654.     return 0;
  655.       else
  656.     return minExpand;
  657.  
  658.     }
  659.     
  660.     /**
  661.      * Returns the String representation of this class...
  662.      */
  663.     public String toString() {
  664.  
  665.     return getClass().getName();
  666.  
  667.     }
  668. }
  669.